#include "tbcore/base/blob.hpp"

#include <tbb/scalable_allocator.h>

#include "tbcore/base/basic_types.hpp"

TB_NAMESPACE_BEGIN

struct Blob::BlobImpl {
  BlobImpl(const char* data, uint32 len) : content(data), length(len) {}
  BlobImpl(const Blob::buffer_const_ptr& buf, uint32 len, uint32 offset) 
    : buffer(buf), content(buffer.get() + offset), length(len) {}
  BlobImpl(const Blob::buffer_ptr& buf, uint32 len, uint32 offset) 
    : buffer(buf), content(buffer.get() + offset), length(len) {}
  BlobImpl(const BlobImpl& rhs) 
    : buffer(rhs.buffer), content(rhs.content), length(rhs.length) {}

  Blob::buffer_const_ptr buffer;
  const char* content;
  uint32 length;
};

Blob::Blob(uint32 len)
  : impl_(new BlobImpl(boost::allocate_shared<char[]>(
    tbb::scalable_allocator<char>(), len), len, 0)) {}

Blob::Blob()
  : impl_(new BlobImpl(nullptr, 0)) {

}

Blob::Blob(const char* str)
	: impl_(new BlobImpl(str, (uint32)strlen(str))) {}

Blob::Blob( const char* data, uint32 len )
  : impl_(new BlobImpl(data, len)) {}

Blob::Blob( const buffer_const_ptr& buffer, uint32 len, uint32 offset /*= 0*/ )
  : impl_(new BlobImpl(buffer, len, offset)) {

}

Blob::Blob( const buffer_ptr& buffer, uint32 len, uint32 offset /*= 0*/ )
  : impl_(new BlobImpl(buffer, len, offset)) {

}

Blob::Blob( const Blob& rhs ) 
  : impl_(new BlobImpl(*rhs.impl_)) {

}

TB::Blob::Blob(Blob &&other) {
  impl_ = other.impl_;
  other.impl_ = nullptr;
}

Blob& TB::Blob::operator=(Blob&& rhs) {
  TB_SAFE_DELETE(impl_);
  impl_ = rhs.impl_;
  return *this;
}

Blob::~Blob() {
  TB_SAFE_DELETE(impl_);
}

void Blob::assign( const Blob& from, uint32 len, uint32 offset /*= 0*/ ) {
  impl_->buffer = from.impl_->buffer;
  impl_->content = from.impl_->content + offset;
  impl_->length = len;
}

void Blob::assign( const char* data, uint32 len ) {
  impl_->content = data;
  impl_->length = len;
}

void Blob::assign( const buffer_const_ptr& buffer, uint32 len, uint32 offset /*= 0*/ )
{
  impl_->buffer = buffer;
  impl_->content = buffer.get() + offset;
  impl_->length = len;
}

void Blob::assign( const buffer_ptr& buffer, uint32 len, uint32 offset /*= 0*/ )
{
  impl_->buffer = buffer;
  impl_->content = buffer.get() + offset;
  impl_->length = len;
}

uint32 Blob::size() const {
  return impl_->length;
}

void Blob::clear() {
  swap(Blob());
}

void Blob::swap(Blob& rhs) {
  std::swap(impl_, rhs.impl_);
}

bool Blob::empty() const {
  return 0 == size();
}

bool Blob::operator==(const Blob& rhs) const {
  return (this == &rhs) ||
    ((impl_->length == rhs.impl_->length) && 
    (0 == ::memcmp(impl_->content, rhs.impl_->content, impl_->length)));
}

Blob::const_iterator Blob::begin() const {
  return impl_->content;
}

Blob::const_iterator Blob::end() const {
  return impl_->content + impl_->length; 
}

const void* Blob::data() const{
  return impl_->content;
}

const char* Blob::content() const {
   return impl_->content;
}

Blob::buffer_const_ptr Blob::buffer() const
{
  return impl_->buffer;
}

Blob& Blob::operator=( const Blob& rhs ) {
  TB_SAFE_DELETE(impl_);
  impl_ = new BlobImpl(*rhs.impl_);
  return *this;
}

void Blob::setSize( uint32 length ) {
  impl_->length = length;
}

Blob Blob::copy() const {
	if (impl_->buffer) {
		return *this;
	} else {
		Blob::buffer_ptr buffer = 
			boost::allocate_shared<char[]>(std::allocator<char>(), impl_->length);
		::memcpy(buffer.get(), impl_->content, impl_->length);
		return Blob(buffer, impl_->length);
	}
}

TB_NAMESPACE_END